﻿//  Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
//  Licensed MIT: http://www.opensource.org/licenses/mit-license.php

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;

namespace DNA.Mvc
{
    /// <summary>
    /// Present the event host,used to dispatch events to observers.
    /// </summary>
    /// <remarks>
    /// Developers should not use the class directly.In order to raise events please inherit the <see cref="EventBase"/>  and <see cref="EventObserver"/>  to raise and handling events.
    /// </remarks>
    public sealed class EventDispatcher : IObservable<EventBase>, IDisposable
    {
        /// <summary>
        /// Notify observer(s) to process the event.
        /// </summary>
        /// <param name="e"></param>
        public static void Process(EventBase e)
        {
            var monitor = new EventDispatcher();
            monitor.ProcessEvent(e);
        }

        private IEnumerable<EventObserver> GetObservers()
        {
            return DependencyResolver.Current.GetServices<EventObserver>();
        }

        public EventDispatcher()
        {
            this.Subscribers = new List<EventObserver>();
            var observers = GetObservers();
            if (observers != null)
            {
                foreach (EventObserver observer in observers)
                {
                    this.Subscribe(observer);
                }
             //   observers.AsParallel().ForAll(os => this.Subscribe(os));
            }
        }

        /// <summary>
        /// Gets the subscribed observers.
        /// </summary>
        public ICollection<EventObserver> Subscribers { get; private set; }

        /// <summary>
        /// Notifity the observer(s) to process the event.
        /// </summary>
        /// <param name="e"></param>
        public void ProcessEvent(EventBase e)
        {
            //Subscribers.AsParallel().ForAll(o =>
            //{
            foreach (var o in Subscribers)
            {
                try
                {
                    ((IObserver<EventBase>)o).OnNext(e);
                }
                catch (Exception ex)
                {
                    ((IObserver<EventBase>)o).OnError(ex);
                }
            }
           // });
        }

        /// <summary>
        /// Add an observer to subscribes list.
        /// </summary>
        /// <param name="observer"></param>
        public void Subscribe(EventObserver observer)
        {
            if (Subscribers.Contains(observer))
                throw new Exception("Observer has registered.");

            Subscribers.Add(observer);
        }

        IDisposable IObservable<EventBase>.Subscribe(IObserver<EventBase> observer)
        {
            var eventObserver = observer as EventObserver;
            if (eventObserver != null)
            {
                this.Subscribe(eventObserver);
                return eventObserver;
            }

            throw new ArgumentNullException("observer");
        }

        public void Dispose()
        {
            Subscribers.Clear();
        }
    }
}
